package com.gitee.wsl.android.ext

import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.drawable.Drawable
import android.location.LocationManager
import android.os.Build
import android.os.Process
import android.os.UserHandle
import android.os.UserManager
import androidx.core.content.getSystemService
import androidx.core.content.pm.PackageInfoCompat
import com.gitee.wsl.android.ext.store.fileProviderAuthority
import com.gitee.wsl.android.imageloader.ImageLoaderOp
import com.gitee.wsl.android.imageloader.Loader
import com.gitee.wsl.service.Alarm
import com.gitee.wsl.service.AlarmServer
import com.gitee.wsl.service.Download
import com.gitee.wsl.service.DownloadService
import com.gitee.wsl.ext.App
import com.gitee.wsl.ext.ApplicationConfig
import com.gitee.wsl.jvm.ext.base.TimeProvider
import com.gitee.wsl.jvm.ext.base.systemTimeProvider
import com.gitee.wsl.jvm.ext.store.formatFileSize
import com.gitee.wsl.data.store.StoreServer
import com.gitee.wsl.data.store.mmkv.MMKVStoreServer
import com.gitee.wsl.eventbus.event4k.Event4k
import com.google.modernstorage.storage.AndroidFileSystem
import com.tencent.mmkv.MMKV
import kotlinx.coroutines.*
import okio.FileSystem
import timber.log.Timber
import java.io.File
import java.text.SimpleDateFormat
import java.util.*

object ApplicationInit : App{

    //val applicationScope:CoroutineScope by ApplicationConfig::applicationScope

    fun init(application: Application, storeServer: StoreServer = MMKVStoreServer(MMKV.defaultMMKV()), eventBus: Event4k=Event4k.default){
        ApplicationInit.application = application
        fileSystem = AndroidFileSystem(application)
        globalStoreServer = storeServer
        this.eventBus=eventBus
    }

    lateinit var application: Application
        internal set

    @SuppressLint("StaticFieldLeak")
    lateinit var  fileSystem : FileSystem
        internal set

    var imageLoader: Loader = ImageLoaderOp

    var globalStoreServer: StoreServer by ApplicationConfig::globalStoreServer

    var eventBus: Event4k by ApplicationConfig::eventBus

    val isDebugModel = Build.TYPE.lowercase(Locale.ROOT).contains("debug") || Build.TYPE.lowercase(Locale.ROOT) == "eng"

    fun relaunchApp(context: Context, killProcess: Boolean = true) =
        application.packageManager.getLaunchIntentForPackage(AppInfo.packageName)?.let {
            it.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
            it.startForActivity(context)
            if (killProcess) Process.killProcess(Process.myPid())
        }

    /**
     * 当前应用分配的最大内存
     *
     * @return
     */
    @JvmStatic
    fun getMaxMemory() = Runtime.getRuntime().maxMemory()

    /**
     * 当前应用分配的总内存
     *
     * @return
     */
     fun getTotalMemory() = Runtime.getRuntime().totalMemory()

    /**
     * 当前应用分配的剩余内存
     *
     * @return
     */
     fun getFreeMemory() = Runtime.getRuntime().freeMemory()

    @SuppressLint("SimpleDateFormat")
    suspend fun dump(context: Context): String {
        val df = SimpleDateFormat("yyyy-MM-dd-HHmmss")
        val file = File(
            context.getExternalFilesDir(null),
            "kvaesitso-log-${df.format(Date(System.currentTimeMillis()))}"
        )
        withContext(Dispatchers.IO) {
            val fos = file.outputStream().writer()
            fos.write("Device: ${Build.DEVICE}\n")
            fos.write("SDK version: ${Build.VERSION.SDK_INT}\n")
            fos.write("====================================\n")
            val input =
                Runtime.getRuntime().exec("/system/bin/logcat -d").inputStream.bufferedReader()
            var line = input.readLine()
            while (line != null) {
                line = input.readLine()
                fos.write("$line\n")
            }
            fos.close()
        }
        return file.absolutePath
    }

    @SuppressLint("SimpleDateFormat")
    suspend fun exportDatabases(context: Context): String {
        val df = SimpleDateFormat("yyyy-MM-dd-HHmmss")
        val exportFile = File(
            context.getExternalFilesDir(null),
            "room-${df.format(Date(System.currentTimeMillis()))}.db"
        )
        withContext(Dispatchers.IO) {
            context.getDatabasePath("room").copyTo(exportFile)
        }
        return exportFile.absolutePath
    }
}


object AppInfo{
    inline val packageName: String get() = ApplicationInit.application.packageName

    inline val packageInfo: PackageInfo
        get() = getPackageInfo(packageName)

    inline val appName: String
        get() = ApplicationInit.application.applicationInfo.loadLabel(ApplicationInit.application.packageManager).toString()

    inline val appIcon: Drawable? get() = packageInfo.applicationInfo?.loadIcon(ApplicationInit.application.packageManager)

    inline val appVersionName: String? get() = packageInfo.versionName

    inline val appVersionCode: Long get() = PackageInfoCompat.getLongVersionCode(packageInfo)

    inline val appSize:String
        get()  = packageInfo.applicationInfo?.publicSourceDir?.let { File(it).formatFileSize}?:"0"


    inline val appInstallTime:String
        get()  {
            val dir = packageInfo.applicationInfo?.sourceDir ?: return ""
            return SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date(File(dir).lastModified()))
        }

    inline val appPath:String? get() = packageInfo.applicationInfo?.sourceDir

    inline val isAppDebug: Boolean get() = ApplicationInit.application.isAppDebug

    inline val isLocationEnabled: Boolean
        get() = ApplicationInit.application.getSystemService<LocationManager>()?.isProviderEnabled(
            LocationManager.GPS_PROVIDER
        ) == true

    inline val isAppDarkMode: Boolean
        get() = (ApplicationInit.application.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES

    fun getPackageInfo(packageName:String):PackageInfo{
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            ApplicationInit.application.packageManager.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(0))
        } else {
            ApplicationInit.application.packageManager.getPackageInfo(packageName,0)
        }
    }

}


inline val Application.isAppDebug: Boolean
    get() = packageManager.getApplicationInfo(packageName, 0).flags and ApplicationInfo.FLAG_DEBUGGABLE != 0

fun UserHandle.getSerialNumber(context: Context): Long {
    if (this == Process.myUserHandle()) return 0L
    val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
    return userManager.getSerialNumberForUser(this)
}

/*fun launchAppSettings(): Boolean =
    Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
        .apply { data = Uri.fromParts("package", packageName, null) }
        .startForActivity()*/



interface LibScope{
    /**
     * 当明使用的文件系统,默认为 AndroidFileSystem
     */
    var fileSystem : FileSystem
    //var isDebugModel:Boolean
    /**
     * 时间获取方式,默认为 本机 DeviceTimeProvider
     */
    var timeProvider: TimeProvider

    /**
     * 用于文件分享 等,应设置
     */
    var appFileProviderAuthority: String

    /**
     * 全局KV
     */
    var globalStoreServer: StoreServer

    /**
     * 通知主题设置 ,有默认
     */
    fun initNotificationTheme(notificationThemeConfig: NotificationTheme.()->Unit)

    /**
     * 下载器设置,暂无默认
     */
    fun initDownLoadService(downloadService: DownloadService)

    /**
     * 定时提醒服务设置,暂无
     */
    fun initAlarmService(alarmService: AlarmServer)

    fun initLoadFonts(configFonts:suspend (fontList:List<File>)->Unit)

    fun grantShowNotification()

    fun crashHandler(handler: CrashCatchListener =object: CrashCatchListener {
        override fun handleException(ex: Throwable):Boolean {
           Timber.d("crash exception ${ex.message}")
           return true
        }

        override fun uncaughtException(context: Context, thread: Thread, ex: Throwable) {
            Timber.d("unCrash exception ${ex.message}")
        }
    })
}

/**
 * detail: 异常捕获处理
 * @author Ttt
 */
interface CrashCatchListener {
    /**
     * 处理异常
     * @param ex [Throwable]
     */
    fun handleException(ex: Throwable):Boolean

    /**
     * 处理未捕获的异常
     * @param context [Context]
     * @param thread  [Thread]
     * @param ex      [Throwable]
     */
    fun uncaughtException(context: Context, thread: Thread, ex: Throwable)
}

fun libInit(application: Application,config: LibScope.()->Unit){
    config(LibScopeImp(application))
}

 open class LibScopeImp(val application: Application): LibScope {

    init {
        MMKV.initialize(application)
        ApplicationInit.init(application)
    }

    override var fileSystem: FileSystem
        get() = ApplicationInit.fileSystem
        set(value) {
            ApplicationInit.fileSystem = value
        }

    override var timeProvider: TimeProvider
        get() = systemTimeProvider
        set(value) {
            systemTimeProvider = value
        }

    override var appFileProviderAuthority: String
        get() =fileProviderAuthority
        set(value) {
            fileProviderAuthority =value
        }
     override var globalStoreServer: StoreServer
         get() = ApplicationInit.globalStoreServer
         set(value) {
             ApplicationInit.globalStoreServer = value
         }

     override fun initNotificationTheme(notificationThemeConfig: NotificationTheme.() -> Unit) {
         NotificationExt.init(notificationThemeConfig)
    }

    override fun initDownLoadService(downloadService: DownloadService) {
        Download.init(downloadService)
    }

    override fun initAlarmService(alarmService: AlarmServer) {
        Alarm.init(alarmService)
    }

    override fun initLoadFonts(configFonts: suspend (fontList: List<File>) -> Unit) {
        runBlocking(Dispatchers.IO){
            val fontFileList=application.getAdditionalFonts()
            configFonts(fontFileList)
        }
    }

    override fun grantShowNotification() {
        if (NotificationExt.canShowNotifications()) return
        Timber.d(" Manifest.permission.POST_NOTIFICATIONS granted")
    }

     override fun crashHandler(handler: CrashCatchListener) {
         val mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()
         // 设置该 CrashUtils 为程序的默认处理器
         Thread.setDefaultUncaughtExceptionHandler { thread, ex ->
             if (!handler.handleException(ex) && mDefaultHandler != null) {
                 // 如果用户没有处理则让系统默认的异常处理器来处理
                 mDefaultHandler.uncaughtException(thread, ex);
             } else {
                 handler.uncaughtException(application, thread, ex);
             }
         }
     }
 }
